/*
 * This file or a portion of this file is licensed under the terms of
 * the Globus Toolkit Public License, found in file GTPL, or at
 * http://www.globus.org/toolkit/download/license.html. This notice must
 * appear in redistributions of this file, with or without modification.
 *
 * Redistributions of this Software, with or without modification, must
 * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
 * some other similar material which is provided with the Software (if
 * any).
 *
 * Copyright 1999-2004 University of Chicago and The University of
 * Southern California. All rights reserved.
 */
package org.griphyn.vdl.toolkit;

import java.io.*;
import java.sql.SQLException;
import java.util.Set;
import java.util.TreeSet;
import org.griphyn.vdl.directive.*;
import org.griphyn.vdl.parser.*;
import org.griphyn.vdl.util.Logging;
import java.util.regex.*;

/**
 * This class encapsulates common helper functions for VDL parsing.
 *
 * @author Jens-S. Vöckler
 * @author Yong Zhao
 * @version $Revision: 50 $
 *
 * @see UpdateVDC
 * @see VDLc
 * @see org.griphyn.vdl.parser.VDLtParser
 * @see org.griphyn.vdl.parser.VDLxParser 
 */
public abstract class VDLHelper extends Toolkit
{
  /**
   * Pattern to match against when recognizing VDLx from VDLt.
   * recognizes: "<?xml " "<definition " "<ns1:definition "
   */
  private static final String c_pattern =     
    "^\\s*<(?:(?:\\w+:)?definitions|\\?\\s*xml)\\s+";

  /**
   * Converter for VDLt to VDLx, is a singleton. 
   */
  private static VDLtConvert c_t2x = null;

  /**
   * ctor: Constructs a new instance object with the given application name.
   *
   * @param appName is the name of the shell wrapper with which to report. 
   */
  public VDLHelper( String appName )
  {
    super(appName);
  }

  /**
   * Default ctor: Constructs a new instance object with the given
   * application name.
   *
   * @param appName is the name of the shell wrapper with which to report. 
   * @param verbosity sets the verbosity level of the "app" logging queue. 
   */
  public VDLHelper( String appName, int verbosity )
  {
    super(appName,verbosity);
  }

  /**
   * Determines if a file is VDLx or VDLt. It does so by
   * expecting the ubiquitous XML preamble in the VDLx. 
   *
   * @param filename is the name of the file
   * @return true if the file is XML, false otherwise.
   */
  public boolean isXML( String filename )
    throws IOException
  {
    LineNumberReader lnr = new LineNumberReader(new FileReader(filename));
    String line = null;
    boolean result = false;
    java.util.regex.Pattern p = java.util.regex.Pattern.compile( VDLHelper.c_pattern );

    while ( (line = lnr.readLine()) != null &&
	    ! result && lnr.getLineNumber() < 64 ) {
      result = p.matcher(line).lookingAt();
    }
    lnr.close();
    return result;
  }

  /**
   * Determines, if input file is VDLt or VDLx. Parses VDLt into a
   * temporary VDLx file. Adds VDLx into the VDC.
   *
   * @param filename is the file to add, either VDLt or VDLx content.
   * @param define is a VDC connector to add to the VDC
   * @param reject is a writer to gather definitions that were reject from
   * the VDC, depending on the overwrite mode. If <code>null</code>, no rejects
   * will be gathered.
   * @param overwrite determines insert or update mode. 
   * @return the result from adding the XML into the VDC
   * @see org.griphyn.vdl.directive.Define#updateVDC( Reader, Writer, boolean )
   */
  public boolean addFileToVDC( String filename, 
			       Define define, 
			       Writer reject, boolean overwrite )
    throws VDLtParserException, VDLtScannerException, IOException
  {
    // instantiate singleton, if necessary
    if ( c_t2x == null ) c_t2x = new VDLtConvert();

    File tempfn = null;
    if ( isXML(filename) ) {
      // VDLx file, no conversions necessary
      tempfn = new File(filename);
    } else {
      // VDLt file, convert to XML
      Reader r = new BufferedReader( new FileReader(filename) );
      tempfn = File.createTempFile( "vdlc-", ".xml", null );
      tempfn.deleteOnExit();
      Writer w = new BufferedWriter( new FileWriter(tempfn) );
      c_t2x.VDLt2VDLx( r, w );
      r.close();
      w.flush();
      w.close();
    }

    return define.updateVDC( new BufferedReader( new FileReader(tempfn) ),
			     reject, overwrite );
  }

  /**
   * Determines, if input file is VDLt or VDLx. Parses VDLt into a
   * temporary VDLx file. Adds VDLx into the VDC. Skips to next file on
   * error.
   *
   * @param args is the argument vector of main
   * @param start is the start of filenames in the argument vector.
   * @param define is a VDC connector to add to the VDC
   * @param reject is a writer to gather definitions that were reject from
   * the VDC, depending on the overwrite mode. If <code>null</code>, no rejects
   * will be gathered.
   * @param overwrite determines insert or update mode. 
   * @return a set of filenames which reported errors.
   * @see #addFileToVDC( String, Define, Writer, boolean )
   */
  public Set addFilesToVDC( String[] args, int start, 
			    Define define, 
			    Writer reject, boolean overwrite )
  {
    Set result = new TreeSet();

    for ( int i = start; i < args.length; ++i ) {
      m_logger.log( "app", 1, "parsing \"" + args[i] + "\"" );

      try { 
	if ( ! addFileToVDC(args[i],define,reject,overwrite) ) {
	  // unsuccessful parsing
	  m_logger.log( "default", 0, "XML parsing error, skipping to next file" );
	  result.add( args[i] );
	}
      } catch ( VDLtParserException e ) {
	m_logger.log( "default", 0, "syntactical error, skipping to next file" );
	System.err.println( e.getMessage() );
	result.add( args[i] );
      } catch ( VDLtScannerException e ) {
	m_logger.log( "default", 0, "lexical error, skipping to next file" );
	System.err.println( e.getMessage() );
	result.add( args[i] );
      } catch ( IOException e ) {
	m_logger.log( "default", 0, "I/O error, skipping to next file" );
	System.err.println( e.getMessage() );
	result.add( args[i] );
      }
    }

    return result;
  }
}
